home *** CD-ROM | disk | FTP | other *** search
- /*---------------------------------------------------------------
- *
- * FILE
- * prs2main.c
- *
- * DESCRIPTION
- * top level PRS2 rule manager routines (called by the executor)
- *
- * INTERFACE ROUTINES
- * prs2main()
- *
- * IDENTIFICATION
- * $Header: /private/postgres/src/rules/prs2/RCS/prs2main.c,v 1.16 1991/11/18 22:21:22 mer Exp $
- * ----------------------------------------------------------------
- */
-
- #include "parser/parse.h" /* RETRIEVE et all are defined here */
- #include "utils/log.h"
- #include "utils/rel.h"
- #include "access/heapam.h"
- #include "access/htup.h"
- #include "storage/buf.h"
- #include "rules/prs2.h"
- #include "rules/prs2stub.h"
- #include "nodes/plannodes.h"
- #include "nodes/execnodes.h" /* which includes access/rulescan.h */
- #include "nodes/execnodes.a.h"
- #include "nodes/mnodes.h"
- #include "utils/mcxt.h"
-
- /*------------------------------------------------------------------
- *
- * prs2main()
- *
- * Activate any rules that apply to the attributes specified.
- *
- *
- * Explanation of arguments:
- * estate: the EState (executor state)
- * relationRuleInfo:
- * If the rule manager is called as part of a 'scan'
- * (i.e. the operation is a 'RETRIEVE' then this contains
- * some useful info about the scanned relation...
- * operation:
- * the kind of operation performed.
- * this can only be a RETRIEVE (everything else is ignored).
- * userId:
- * the user Id (currently ignored).
- * relation:
- * the relation where oldTuple & newTuple belong.
- * oldTuple:
- * If the operation is a RETRIEVE this is the base tuple, as
- * returned form the access methods (i.e. no rules have been applied
- * yet).
- * If the operation is a DELETE then this is the tuple to be
- * deleted.
- * If the operation is a REPLACE then this is the tuple
- * that is going to be replace with the new tuple.
- * oldBuffer:
- * the Buffer of the oldTuple.
- * newTuple:
- * If the operation is a APPEND then this is the new tuple
- * to be appended.
- * If the operation is a REPLACE then this is the new version
- * of the tuple (the one that will replace oldTuple).
- * newBuffer:
- * the Buffer of the newTuple.
- * rawTuple:
- * this is the same as the oldTuple, but exactly as returned
- * but the access methods. Normally, when a tuple is retrieved,
- * it is passed to the rule manager and if there are some
- * backward chaining rules defined on it, some of its attribute
- * values will be changed. If the final operation was a replace,
- * then oldTuple will be this changed tuple, ad rawTuple will be the
- * tuple, as it was returned by the AM with no rule activations.
- * 'rawTuple' is a valid tuple if the operation is REPLACE,
- * and it is ignored in all other cases.
- * rawBuffer:
- * the buffer of the rawTuple.
- * attributeArray
- * An array of AttributeNumber. These are the attributes that
- * are affected by the specified operation.
- * If the operation is a RETRIEVE or a REPLACE then these
- * are the attributes that are retrieved or replaced.
- * In the xcase of APPEND & DELETE we assume that all the
- * attributes of the tuple are affected.
- * numberOfAttributes
- * the number of attributes in attributeArray.
- * returnedTupleP
- * If the operation was a RETRIEVE and the value of the
- * oldTuple has changed because of a rule, then this is
- * the new tuple (that has to be used by the executor in
- * the place of the oldTuple.
- * If the operation was a REPLACE or APPEND and newTuple had
- * to be changed because of a rule, then this is the new tuple
- * that the executor has to use in the place of newTuple.
- * returnedBufferP
- * The Buffer of the returnedTuple (usually InvalidBuffer).
- *
- * Returned values:
- * PRS2_STATUS_TUPLE_UNCHANGED
- * Probably no applicable rules were found. Proceed as normal
- * returnedTupleP and returnedBufferP should NOT be used.
- * PRS2_STATUS_TUPLE_CHANGED
- * One or more rules were activated, (*returnedTupleP) should
- * be used (instead of oldTuple or newTuple).
- * PRS2_STATUS_INSTEAD
- * An instead was specified in the action part of a rule,
- * and therefore the executor must do nothing.
- *
- */
-
- Prs2Status
- prs2Main(estate, retrieveRelationRuleInfo, operation, userId, relation,
- oldTuple, oldBuffer,
- newTuple, newBuffer,
- rawTuple, rawBuffer,
- attributeArray, numberOfAttributes,
- returnedTupleP, returnedBufferP)
- EState estate;
- RelationRuleInfo retrieveRelationRuleInfo;
- int operation;
- int userId;
- Relation relation;
- HeapTuple oldTuple;
- Buffer oldBuffer;
- HeapTuple newTuple;
- Buffer newBuffer;
- AttributeNumberPtr attributeArray;
- int numberOfAttributes;
- HeapTuple *returnedTupleP;
- Buffer *returnedBufferP;
- {
- Prs2Status status;
- Buffer localBuffer;
- Prs2EStateInfo prs2EStateInfo;
- RelationRuleInfo updateRelationRuleInfo;
- int topLevel;
- Relation explainRelation;
- MemoryContext oldMemContext;
- GlobalMemory prs2MemContext;
-
- if (relation == NULL) {
- /*
- * This should not happen!
- */
- elog(WARN, "prs2main: called with relation = NULL");
- }
-
- if (returnedBufferP == NULL) {
- returnedBufferP = &localBuffer;
- }
-
- explainRelation = get_es_explain_relation(estate);
-
- prs2EStateInfo = get_es_prs2_info(estate);
- if (prs2EStateInfo == NULL) {
- /*
- * well, what do you know...
- * this is the first time we call the rule manager and
- * we are at the topmost level (i.e. no recursions
- * yet. Allocate memory for the stack & initialize it.
- */
- prs2EStateInfo = prs2RuleStackInitialize();
- set_es_prs2_info(estate, prs2EStateInfo);
- }
-
- /*
- * in order to avoid memory leaks, we will switch to another
- * memory context, and after the dust settles we will just
- * destroy all memory palloced but not pfreed.
- *
- * NOTE: this works just fine even if we have recursive calls
- * to the executor/rule manager (even if the name of the
- * new context is the same - it is never used...)
- */
- prs2MemContext = CreateGlobalMemory("*prs2Main");
- oldMemContext = MemoryContextSwitchTo((MemoryContext)prs2MemContext);
-
- switch (operation) {
- case RETRIEVE:
- status = prs2Retrieve(
- prs2EStateInfo,
- retrieveRelationRuleInfo,
- explainRelation,
- oldTuple,
- oldBuffer,
- attributeArray,
- numberOfAttributes,
- relation,
- returnedTupleP,
- returnedBufferP);
- break;
- case DELETE:
- updateRelationRuleInfo = get_es_result_rel_ruleinfo(estate);
- status = prs2Delete(
- prs2EStateInfo,
- updateRelationRuleInfo,
- explainRelation,
- oldTuple,
- oldBuffer,
- rawTuple,
- rawBuffer,
- relation);
- break;
- case APPEND:
- updateRelationRuleInfo = get_es_result_rel_ruleinfo(estate);
- status = prs2Append(
- prs2EStateInfo,
- updateRelationRuleInfo,
- explainRelation,
- newTuple,
- newBuffer,
- relation,
- returnedTupleP,
- returnedBufferP);
- break;
- case REPLACE:
- updateRelationRuleInfo = get_es_result_rel_ruleinfo(estate);
- status = prs2Replace(
- prs2EStateInfo,
- updateRelationRuleInfo,
- explainRelation,
- oldTuple,
- oldBuffer,
- newTuple,
- newBuffer,
- rawTuple,
- rawBuffer,
- attributeArray,
- numberOfAttributes,
- relation,
- returnedTupleP,
- returnedBufferP);
- break;
-
- default:
- elog(WARN, "prs2main: illegal operation %d", operation);
-
- } /* switch */
-
- /*
- * switch back to the old memory context
- * and destroy all memory allocated under the prs2MemContext.
- *
- * NOTE: VERY IMPORTANT!
- * If we have changed the tuple,then this new "returnedTuple"
- * has been palloced under the 'prs2MemCOntext'.
- * So, we have to recopy it under the other context for it
- * to survive and live a long, happy life.
- *
- * Probably the right solution would be to switch temporarilly
- * to the old context and then back to prs2MemContext when we
- * were creating this returned tuple in the first place,
- * but the code will become more complex and difficult to
- * understand (while -of course- now it is a paradigm of
- * simplicity and elegance....)
- * Thus we could avoid this extra copy (so what, it is
- * only a simple bcopy anyway....)
- */
- (void) MemoryContextSwitchTo(oldMemContext);
- if (status == PRS2_STATUS_TUPLE_CHANGED) {
- *returnedTupleP = palloctup(*returnedTupleP,
- *returnedBufferP,
- relation);
- }
- GlobalMemoryDestroy(prs2MemContext);
-
- /*
- * XXX: do we HAVE to call prs2RuleStackFree ??
- * I guess not really, we can just reuse the same stack
- * over and over again...
- */
- /* if (topLevel) {
- * prs2RuleStackFree(prs2EStateInfo);
- * }
- */
-
- return(status);
- }
-
- /*---------------------------------------------------------------------
- * prs2MustCallRuleManager
- *
- * return true if the rule manager needs to be called, false
- * otherwise (i.e. if there are absolutely no rules defined!).
- *
- * The main reason for this routine is for the executro to know whether
- * is should call or not the rule manager. If there is no need to do so
- * the executor can avoid setting up all the information the rule
- * manager needs thus making things go faster.
- *
- * NOTE: in case of doubt, return true!
- * NOTE2: we might make this routine slightly more clever.
- * For instance even if there are some locks, they might not be relevant
- * to the operation currently performed.
- *
- *---------------------------------------------------------------------
- */
- bool
- prs2MustCallRuleManager(relationRuleInfo, oldTuple, oldBuffer, operation)
- RelationRuleInfo relationRuleInfo;
- HeapTuple oldTuple;
- Buffer oldBuffer;
- int operation;
- {
-
- bool relLocksFlag;
- bool oldTupLocksFlag;
- bool stubsFlag;
-
- if (prs2GetNumberOfLocks(relationRuleInfo->relationLocks)==0)
- relLocksFlag = false;
- else
- relLocksFlag = true;
-
- /*
- * check for locks in 'old' tuple.
- * However, if this is a tuple of the "pg_class" relation,
- * ignore these locks.
- */
- if (oldTuple != NULL && !relationRuleInfo->ignoreTupleLocks)
- oldTupLocksFlag = !(HeapTupleHasEmptyRuleLock(oldTuple, oldBuffer));
- else
- oldTupLocksFlag = false;
-
- if (relationRuleInfo->relationStubs == NULL ||
- prs2StubIsEmpty(relationRuleInfo->relationStubs))
- stubsFlag = false;
- else
- stubsFlag = true;
-
-
- switch (operation) {
- case RETRIEVE:
- if (relLocksFlag || oldTupLocksFlag)
- return(true);
- else
- return(false);
- break;
- case DELETE:
- if (relLocksFlag || oldTupLocksFlag)
- return(true);
- else
- return(false);
- case APPEND:
- if (stubsFlag || relLocksFlag)
- return(true);
- else
- return(false);
- break;
- case REPLACE:
- if (stubsFlag || relLocksFlag || oldTupLocksFlag)
- return(true);
- else
- return(false);
- break;
- default:
- elog(WARN, "prs2MustCallRuleManager: illegal operation %d",
- operation);
-
- } /* switch */
-
- /*
- * keep lint happy
- */
-
- return(false);
- }
-
- /*========================== SHOW STATS ==========================*/
- /*
- * We keep the following info:
- *
- * rulesTested:
- * Number of rules tested (i.e. how many times we tested the
- * qualification of a rule).
- * rulesActivated:
- * Number of rules that were activated, i.e. the number of rules
- * that had their qualification evaluated to true.
- */
-
-
- int Prs2Stats_rulesActivated;
- int Prs2Stats_rulesTested;
-
- /*------------------------------------------------------------------
- * ResetPrs2Stats
- *------------------------------------------------------------------
- */
- void
- ResetPrs2Stats()
- {
- Prs2Stats_rulesTested = 0;
- Prs2Stats_rulesActivated = 0;
- }
-
-
- /*------------------------------------------------------------------
- * ShowPrs2Stats
- *------------------------------------------------------------------
- */
- void
- ShowPrs2Stats(statfp)
- FILE *statfp;
- {
-
- fprintf(statfp, "!\t%d rules_tested %d rules_activated\n",
- Prs2Stats_rulesTested,
- Prs2Stats_rulesActivated);
- }
-
-